package gapidt.nlp;

import java.util.ArrayList;
import java.util.List;

import edu.stanford.nlp.parser.lexparser.LexicalizedParser;
import edu.stanford.nlp.trees.Tree;

/**
 * Esta clase representa un Parser de Lenguaje Natural y provee funcionalidad 
 * para reconocer partes del lenguaje (Part-Of-Speech Tagging).
 * 
 * @author Alejandro Durante
 */
public class SemanticParser {

	public static String LEXICALIZED_PARSER_FILE_PROPERTY = "gapidt.nlp.parserFile";
	
	private static LexicalizedParser lexicalizedParser;
	private static List<String> relevantWordTags = new ArrayList<String>();
	
	public static void init(){
		
		//Inicializar el lexicalized parser (Stanford Parser)
		if(lexicalizedParser == null){
			String parserFileUrl = System.getProperty(LEXICALIZED_PARSER_FILE_PROPERTY); 
			lexicalizedParser = new LexicalizedParser(parserFileUrl);
		}
		
		//Inicializar el conjunto de tags de palabras relevantes
		if(relevantWordTags.isEmpty()){
			relevantWordTags.add("NN"); // Noun, singular or mass 
			relevantWordTags.add("NNS"); // Noun, plural 
			relevantWordTags.add("NNP"); // Proper Noun, singular 
			relevantWordTags.add("NNPS"); // Proper Noun, plural
			
			relevantWordTags.add("VB"); // Verb, base form, subsumes imperatives, infinitives and subjunctives
			relevantWordTags.add("VBD"); // Verb, past tense includes the conditional form of the verb to be
			relevantWordTags.add("VBG"); // Verb, gerund or persent participle 
			relevantWordTags.add("VBN"); // Verb, past participle 
			relevantWordTags.add("VBP"); // Verb, non-3rd person singular present 
			relevantWordTags.add("VBZ"); // Verb, 3rd person singular present 

			relevantWordTags.add("FW"); // Foreign Word
		}
	}
	
	/**
	 * Crea el árbol sintáctico del texto dado. 
	 */
	public static Tree parseSemanticTree(String text){
		init();
		return lexicalizedParser.apply(text);
	}
	
	/**
	 * Retorna la lista de palabras del texto dado indicando que función
	 * cumple cada una. 
	 */
	public static List<Word> parseSemantic(String text){
		init();
		Tree tree = lexicalizedParser.apply(text);
		List<Word> wordsList = new ArrayList<Word>();
		flattenTree(tree, wordsList);
		return wordsList;
	}
	
	/**
	 * Convierte un árbol en una lista de palabras (Word)
	 */
	protected static void flattenTree(Tree node, List<Word> list){
		if(node == null)
			return;
		
		for(Tree child : node.children()){
			if(child.isLeaf()){
				list.add(new Word(child.label().value(), node.label().value()));
			}else{
				flattenTree(child, list);
			}
		}
	}
	
	/**
	 * Dada una sentencia, devuelve las palabras relevantes (verbos, sustantivos, etc.)
	 */
	public static List<Word> getRelevantWords(String text){
		Tree semanticTree = parseSemanticTree(text);
		List<Word> relevantWords = new ArrayList<Word>();
		searchRelevantWords(semanticTree, relevantWords);
		return relevantWords;
	}
	
	/**
	 * Busca las palabras relevantes en el árbol sintáctico dado
	 */
	private static void searchRelevantWords(Tree node, List<Word> foundWords){
		if(!node.isLeaf()){
			Tree [] children = node.children();
			if(children != null)
				for(int i = 0; i < children.length; i++){
					Tree child = children[i];
					if(child.isLeaf() && isRelevantWordTag(node.label().value()))
						foundWords.add(new Word(child.label().value(), node.label().value()));
					else
						searchRelevantWords(child, foundWords);
				}
		}

	}

	/**
	 * Determina si un tipo de palabra es relevante
	 */
	private static boolean isRelevantWordTag(String wordTag){
		return relevantWordTags.contains(wordTag);
	}
}
